package cn.yj.core.business.service;

import cn.yj.admin.ConsVal;
import cn.yj.annotation.pagehelper.page.Page;
import cn.yj.api.FileUploadHandler;
import cn.yj.cgs.dba.entity.Customer;
import cn.yj.cgs.dba.entity.Message;
import cn.yj.cgs.dba.entity.vo.CustomVo;
import cn.yj.cgs.dba.entity.vo.InviteesRanking;
import cn.yj.cgs.dba.mapper.CustomerMapper;
import cn.yj.cgs.dba.mapper.IntegralWaterMapper;
import cn.yj.common.Enums;
import cn.yj.commons.StringPools;
import cn.yj.commons.utils.CheckUtils;
import cn.yj.commons.utils.MapUtils;
import cn.yj.commons.utils.StringUtils;
import cn.yj.config.SchoolCBDConfig;
import cn.yj.core.business.service.iservice.IIntegralService;
import cn.yj.core.business.service.iservice.IMessageService;
import cn.yj.entity.Base64Entity;
import cn.yj.params.check.annotation.CheckObjectValue;
import cn.yj.params.check.annotation.KeyValue;
import cn.yj.params.check.annotation.Require;
import cn.yj.tools.exception.ServiceException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;
import java.util.Set;

/**
 * @author 永健
 * @since 2021-04-19 17:51
 */
@Service
public class CustomerService extends ServiceImpl<CustomerMapper, Customer> {

    @Resource
    private IntegralWaterMapper integralWaterMapper;

    @Resource
    private IIntegralService integralService;

    @Resource
    private IMessageService messageService;

    @Resource
    private SchoolCBDConfig config;

    public String getOpenIdByUserCode(String userCode){
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getUserCode,
                userCode).select(Customer::getMiniOpenId));
        checkNull(customer, String.format("openid 为 %s 的用户不存在",userCode));
        return customer.getMiniOpenId();
    }

    public String getUserCodeByOpenId(String openId){
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getMiniOpenId,
                openId).select(Customer::getUserCode));
        checkNull(customer, String.format("openid 为 %s 的用户不存在",openId));
        return customer.getUserCode();
    }

    public String getCustomerSchoolByMiniOpenId(@Require String openId) {
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getMiniOpenId,
                openId).select(Customer::getSchoolName));
        checkNull(customer, String.format("openid 为 %s 的用户不存在",openId));
        return customer.getSchoolName();
    }

    public String getCustomerSchoolByCode(@Require String customCode) {
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getUserCode,
                customCode).select(Customer::getSchoolName));
        checkNull(customer, String.format("customCode 为 %s 的用户不存在", customCode));
        return customer.getSchoolName();
    }

    public String getCityByCode(@Require String customCode) {
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getUserCode,
                customCode).select(Customer::getCity));
        checkNull(customer, String.format("customCode 为 %s 的用户不存在", customCode));
        return customer.getCity();
    }


    public Customer getCustomByCode(@Require String customCode) {
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getUserCode,
                customCode));
        checkNull(customer, String.format("customCode 为 %s 的用户不存在", customCode));
        return customer;
    }

    public Customer getConcatInfoByCode(@Require String customCode) {
        Customer customer = baseMapper.selectConcatInfo(customCode);
        checkNull(customer, String.format("customCode 为 %s 的用户不存在", customCode));
        return customer;
    }

    /**
     * 获取邀请码url链接
     *
     * @param userCode 用户编码
     */
    public String getInviteQrCode(@Require String userCode) {
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getUserCode,
                userCode).select(Customer::getInviteQrCode));
        if (StringUtils.isNull(customer)) {
            throw new ServiceException(String.format("找不到用户信息：%s", userCode));
        }
        return customer.getInviteQrCode();
    }

    /**
     * 获取用户的邀请人邀请码
     *
     * @param userCode 用户编码
     */
    public String getInviteesByUserCode(String userCode) {
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getUserCode,
                userCode));
        checkNull(customer, String.format("customCode 为 %s 的用户不存在", userCode));
        return customer.getInvitees();
    }

    /**
     * 根据用户编码获取用户的邀请码
     *
     * @param userCode 用户编码
     *
     * @return 邀请人的邀请码
     */
    public String getInviteCode(@Require String userCode) {
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getUserCode,
                userCode).select(Customer::getInviteCode));
        if (StringUtils.isNull(customer)) {
            throw new ServiceException(String.format("找不到用户信息：%s", userCode));
        }
        return customer.getInviteCode();
    }


    /**
     * 学生是否完善了信息
     *
     * @param customCode 用户编码
     */
    public boolean authenticated(@Require String customCode) {
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getUserCode,
                customCode).select(Customer::getAuthenticated));
        checkNull(customer, String.format("customCode 为 %s 的用户不存在", customCode));
        return customer.getAuthenticated() > 0;
    }


    public boolean exitsByMiniOpenId(@Require String openid) {
        return baseMapper.selectCount(new QueryWrapper<Customer>().lambda().eq(Customer::getMiniOpenId, openid)) > 0;
    }

    public boolean exitsByInviteCode(@Require String code) {
        return baseMapper.selectCount(new QueryWrapper<Customer>().lambda().eq(Customer::getInviteCode, code)) > 0;
    }

    public Customer getCustomByMiniOpenId(@Require String openid) {
        return baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getMiniOpenId, openid));
    }

    public CustomVo getCustomVoByMiniOpenId(@Require String openid) {
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getMiniOpenId,
                openid));
        if (StringUtils.isNull(customer)) {
            return null;
        }
        CustomVo customVo = new CustomVo();
        BeanUtils.copyProperties(customer, customVo);
        return customVo;
    }

    private void checkInviteCode(String inviteCode) {
        if (StringUtils.isNotBlank(inviteCode)) {
            if (StringUtils.isBlank(getUserCodeByInviteCode(inviteCode))) {
                // 邀请码不存在
                throw new ServiceException(String.format("邀请码不存在:%s", inviteCode));
            }
        }
    }


    @CheckObjectValue(keyValue = @KeyValue(type = Customer.class, name = {"userCode", "city", "realName", "userPhone"
            , "schoolName"}))
    @Transactional
    public boolean updateByUserCode(Customer customer) {
        if (!CheckUtils.isPhoneLegal(StringUtils.trimAll(customer.getUserPhone()))) {
            throw new ServiceException("请输入正确的手机号码");
        }
        // 邀请码验证
        checkInviteCode(customer.getInvitees());

        // 学号验证
        Customer user = baseMapper.exitStudentCode(customer.getSchoolName(), customer.getStudentCode());
        if (user != null && !user.getUserCode().equals(customer.getUserCode())) {
            throw new ServiceException(String.format("该学号已存在:%s", customer.getStudentCode()));
        }

        // 证件上传处理
        String idCardBase64 = customer.getIdCard();
        if (!StringUtils.isBlank(idCardBase64) && !StringUtils.startsWith(idCardBase64, "http")) {
            try {
                Base64Entity base64Entity = new Base64Entity(idCardBase64, Enums.Minio.Bucket.idCard.name().concat(
                        "/").concat(customer.getUserCode().concat(StringPools.JPG)));
                base64Entity.setContentType(Enums.Minio.ContentType.IMG.getContentType());
                String idCardUrl = FileUploadHandler.getInstant().upload(base64Entity);
                customer.setIdCard(idCardUrl.concat("?v=" + new Date().getTime()));
            } catch (Exception e) {
                throw new ServiceException(String.format("证件上传异常：%s", e.getMessage()));
            }
        }
        // 是否完成认证
        checkPerfect(customer);

        // 积分更新
        handlerIntegral(customer);

        return baseMapper.update(customer, new UpdateWrapper<Customer>().lambda().eq(Customer::getUserCode,
                customer.getUserCode())) > Enums.Common.ZERO;
    }

    private void checkPerfect(Customer customer) {
        String schoolName = customer.getSchoolName();
        String userPhone = customer.getUserPhone();
        if (StringUtils.isBlank(schoolName) || StringUtils.isBlank(userPhone)) {
            return;
        }
        customer.setAuthenticated(Enums.Common.TRUE_V);
    }


    /**
     * 根据邀请码获取用户编码
     *
     * @param inviteCode 邀请码
     *
     * @return 用户编码
     */
    public String getUserCodeByInviteCode(String inviteCode) {
        Customer invitees = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getInviteCode,
                inviteCode).select(Customer::getUserCode));
        if (StringUtils.isNull(invitees)) {
            // 邀请码不存在
            return StringPools.EMPTY;
        }
        return invitees.getUserCode();
    }

    private Object handlerIntegral = new Object();

    private void handlerIntegral(Customer customer) {
        synchronized (handlerIntegral) {
            // 本人积分 是否已经注册过
            SchoolCBDConfig.IntegralConfig integralConfig = config.getIntegralConfig();
            if (integralWaterMapper.exitsBySource(customer.getUserCode(), Enums.IntegralWater.SOURCE.REGISTER.name()) <= Enums.Common.ZERO) {
                integralService.updateIntegral(customer.getUserCode(), integralConfig.getRegister(), true,
                        Enums.IntegralWater.SOURCE.REGISTER, customer.getInviteCode());
            }


            // 判断是否已经有邀请码了
            String inviteesDb = getInviteesByUserCode(customer.getUserCode());
            if (StringUtils.isNotBlank(inviteesDb)) {
                customer.setInvitees(null);
                return;
            }

            // 数据库里没有邀请人
            String invitees = customer.getInvitees();
            if (!StringUtils.isBlank(invitees)) {

                // 邀请码不能是自己的
                String myInviteCode = getInviteCode(customer.getUserCode());
                if (customer.getInvitees().equals(myInviteCode)) {
                    throw new ServiceException("邀请人不能是自己");
                }

                // 我的邀请人不能是我的下级
                checkInviteesIsMyFans(getInviteCode(customer.getUserCode()), customer.getInvitees());

                String inviteesUserCode = getUserCodeByInviteCode(invitees);
                messageService.notice(new Message("您的好友【 " + getNickName(customer.getUserCode()) + " 】完成了您的邀请", "邀请通知", inviteesUserCode));
                // 没有邀请码的话 当前邀请人获得积分
                integralService.updateIntegral(inviteesUserCode, integralConfig.getShare(), true,
                        Enums.IntegralWater.SOURCE.INVITE, null);
            }
        }
    }


    private void checkNull(Object object, String msg) {
        if (StringUtils.isNull(object)) {
            throw new ServiceException(msg);
        }
    }

    @CheckObjectValue(keyValue = @KeyValue(type = Customer.class, name = {"userCode", "city"}))
    public boolean updateCityByUserCode(Customer customer) {
        return baseMapper.update(customer, new UpdateWrapper<Customer>().lambda().eq(Customer::getUserCode,
                customer.getUserCode())) > Enums.Common.ZERO;
    }

    public String updateAvatar(@Require String avatar, @Require String userCode) {
        try {
            Base64Entity base64Entity = new Base64Entity(avatar,
                    Enums.Minio.Bucket.avatar.name().concat("/").concat(ConsVal.AVATAR_PREFIX.concat(userCode).concat(StringPools.JPG)));
            base64Entity.setContentType(Enums.Minio.ContentType.IMG.getContentType());
            String avatarPath = FileUploadHandler.getInstant().upload(base64Entity);
            Customer customer = new Customer();
            customer.setUserCode(userCode);
            customer.setAvatar(avatarPath);
            if (baseMapper.update(customer, new UpdateWrapper<Customer>().lambda().eq(Customer::getUserCode,
                    userCode)) > Enums.Common.ZERO) {
                return customer.getAvatar();
            }
        } catch (Exception e) {
            throw new ServiceException(String.format("头像上传异常：%s", e.getMessage()));
        }
        throw new ServiceException("更新失败");
    }

    public Page<InviteesRanking> inviteesRankingList(Page<InviteesRanking> page, Map<String, Object> params) {
        baseMapper.inviteesRankingList(params, page);
        return page;
    }

    public InviteesRanking getMyRanking(@Require String userCode){
        return baseMapper.selectMyRanking(userCode);
    }

    /**
     * 我邀请的人
     *
     * @param userCode 我的用户编码
     */
    public Page<InviteesRanking> myInvitees(Page<InviteesRanking> page, String userCode) {
        baseMapper.selectCustomerByInvitees(userCode, page);
        return page;
    }

    public long countEffectiveCustomer(@Require String userCode) {
        return baseMapper.countEffectiveCustomer(userCode);
    }

    @Transactional
    @CheckObjectValue(keyValue = @KeyValue(type = Map.class, name = {"invitees"}))
    public boolean updateInvitees(@Require String userCode, Map<String, Object> params) {
        // 是否已经存在邀请人
        String inviteesDb = getInviteesByUserCode(userCode);
        if (StringUtils.isNotBlank(inviteesDb)) {
            throw new ServiceException("您已有邀请人");
        }

        String invitees = MapUtils.getStringVal(params, "invitees");
        checkInviteCode(invitees);

        String inviteesUserCode = getUserCodeByInviteCode(invitees);
        if (StringUtils.isBlank(inviteesUserCode)) {
            throw new ServiceException("邀请码不合法");
        }

        // 邀请码不能是自己的
        String myInviteCode = getInviteCode(userCode);
        if (invitees.equals(myInviteCode)){
            throw new ServiceException("邀请人不能是自己");
        }


        // 我的邀请人不能是我的下级
        checkInviteesIsMyFans(getInviteCode(userCode), invitees);


        super.update(new Customer().setInvitees(invitees),
                new UpdateWrapper<Customer>().lambda().eq(Customer::getUserCode, userCode));


        messageService.notice(new Message("您的好友 " + getNickName(userCode) + " 完成了您的邀请", "邀请通知", inviteesUserCode));
        return integralService.updateIntegral(inviteesUserCode, config.getIntegralConfig().getShare(), true,
                Enums.IntegralWater.SOURCE.INVITE, null);
    }

    private String getNickName(String userCode) {
        Customer customer = baseMapper.selectOne(new QueryWrapper<Customer>().lambda().eq(Customer::getUserCode,
                userCode).select(Customer::getNickName));
        if (StringUtils.isNull(customer)) {
            throw new ServiceException("找不到用户：" + userCode);
        }
        return customer.getNickName();
    }

    /**
     * 校验邀请人不能是自己的下级
     *
     * @param myInviteCode 我的邀请码
     * @param invitees     邀请人
     */
    private void checkInviteesIsMyFans(String myInviteCode, String invitees) {
        Set<String> inviteCodes = baseMapper.selectFollowersInviteCode(myInviteCode);
        if (inviteCodes.contains(invitees)) {
            throw new ServiceException("邀请人不能是自己的下级");
        }
    }


    public boolean exitsByUserCode(@Require String userCode) {
        return baseMapper.selectCount(new QueryWrapper<Customer>().lambda().eq(Customer::getUserCode, userCode)) > Enums.Common.ZERO;
    }
}
